Repository navigation
Conversation
Cmd+V with a copied picture in a session's terminal did nothing in the Mac
app, while the same server worked in a browser.
Codeman's Cmd+V handler (_handleImagePaste, src/web/public/image-input.js)
focuses a hidden contenteditable trap and calls
document.execCommand('paste') before the key's own paste lands there.
Chromium refuses that call at once. From a WKWebView, WebKit answers it with
its DOM paste access menu ("Paste", opened at the last mouse-down) and holds
the page on a synchronous request until the menu is picked or the clipboard
changes. The key's own paste, the one that carries the image, was stuck
behind it.
Measured on macOS 27.2 (WebKit 22625) with a throwaway WKWebView probe:
- a menu Paste hands the page the image as a File for PNG data, a
TIFF+PNG screenshot and a file copied in Finder, so WebKit itself is fine;
- Cmd+V through the same trap hung until the next clipboard change;
- with execCommand('paste') answered false, six Cmd+V presses in a row
(image data, screenshot, Finder file, text, two repeats) each delivered
exactly one paste event to the trap.
The fix lives in the app's HOST_SHIM, which every server page already gets:
it wraps Document.prototype.execCommand so 'paste' returns false the way
Chromium answers it, and every other command reaches WebKit unchanged. The
web page keeps its execCommand call: src/web/CLAUDE.md keeps it for mobile
engines that need it, and doing it in the app also covers servers on older
Codeman versions.
test/desktop-host-shim-paste.test.ts runs the shim's real text, cut out of
assets.rs, in a vm with a stub Document. shim_keeps_its_contract pins the
override too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
In Codeman for Mac (
packages/desktop, the native app: Rust, tao + wry/WKWebView), pressing ⌘V with a copied picture in a session's terminal did nothing. The same server in a browser uploaded the picture and typed its path into the prompt.Why
Codeman's ⌘V handler (
_handleImagePasteinsrc/web/public/image-input.js) moves focus to a hiddencontenteditable"paste trap", callsdocument.execCommand('paste'), and then lets the key's own paste land on the trap, where it reads image files out of the paste event. Chromium refuses theexecCommandcall at once, so only the key's paste happens.From a WKWebView, WebKit handles that call differently. It opens its DOM paste access menu (a small "Paste" menu at the last mouse-down) and holds the page on a synchronous request until the menu is picked or the clipboard changes. The key's own paste, the one that carries the image, was stuck behind it.
Measured on macOS 27.2 (WebKit 22625) with a throwaway WKWebView test app that runs the same flow:
Filefor PNG data, a TIFF+PNG screenshot and a file copied in Finder, so WebKit's clipboard itself is fine.execCommand('paste')answeredfalse, six ⌘V presses in a row (image data, screenshot, Finder file, text, two repeats) each delivered exactly one paste event to the trap.The fix
The app already injects a script into every server page (
HOST_SHIMinpackages/desktop/src/assets.rs). It now wrapsDocument.prototype.execCommandso that'paste'returnsfalse, which is what Chromium answers. Every other command reaches WebKit unchanged.The web page keeps its
execCommand('paste')call.src/web/CLAUDE.mdanddocs/architecture-invariants.mdkeep it for mobile engines that need it. Fixing it in the app also covers servers on older Codeman versions, since the app is a client of whatever server it connects to.Tests
test/desktop-host-shim-paste.test.ts(new, in the default gate) runs the shim's real text, cut out ofassets.rs, in a vm with a stubDocument:pasteanswersfalsewithout calling WebKit, other commands pass through with their arguments andthis, and a page that is not http(s) is left alone.shim_keeps_its_contract(Rust) pins the override.cargo fmt --checkand clippy with-D warningsare clean (cross-checked foraarch64-apple-darwin). The fullcargo testpassed on the Mac mini (303 passed, 9 ignored) with the same change on the server-tabs branch (feat(desktop): server tabs, several Codeman servers open in one window #816).Changeset included (patch).